#!/usr/bin/python
# vi: et sw=2
#

from lib_pulseaudio import *

import pygtk
import gtk
import gobject
import sys

class PulseGlib(gobject.GObject):

  __gsignals__ = {
    "sink-new":         (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT]),
    "sink-input-new":   (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT]),
    "sink-change":      (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT]),
    "sink-input-change":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT]),
    "sink-remove":      (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT]),
    "sink-input-remove":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT]),
    "sink-info":        (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT, gobject.TYPE_INT]),
    "sink-input-info":  (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_PYOBJECT, gobject.TYPE_INT]),
    "context-success":  (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [gobject.TYPE_INT]),
    "context-connecting":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [ ]),
    "context-authorizing":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [ ]),
    "context-setting-name":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [ ]),
    "context-ready":    (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [ ]),
    "context-failed":   (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [ ]),
    "context-terminated":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                                [ ])
  }

  def __init__(self):
    gobject.GObject.__init__(self)

    #print "PulseGlib:"

    self.client_name        = "PyPulseGlib"

    self.pa_mainloop_p      = pa_glib_mainloop_new(None)
    self.pa_mainloop_api_p  = pa_glib_mainloop_get_api(self.pa_mainloop_p)
    self.pa_context_p       = None
    self.pa_proplist_p      = None

    self.pa_state_notify_cb = PA_STATE_NOTIFY_CB_T(self.on_state_notify_cb)
    self.pa_signal_cb       = PA_SIGNAL_CB_T(self.on_signal_cb)
    self.pa_context_success_cb = \
                PA_CONTEXT_SUCCESS_CB_T(self.on_context_success_cb)
    self.pa_context_subscribe_cb = \
                PA_CONTEXT_SUBSCRIBE_CB_T(self.on_context_subscribe_cb)
    self.pa_sink_info_cb    = PA_SINK_INFO_CB_T(self.on_sink_info_cb)
    self.pa_sink_input_info_cb = \
                PA_SINK_INPUT_INFO_CB_T(self.on_sink_input_info_cb)

    self.subscribe_done     = False

    if self.pa_mainloop_api_p == None:
      print "pa_mainloop_api is None"
      sys.exit(1)

    # Initialize signals
    if pa_signal_init(self.pa_mainloop_api_p) != 0:
      print "pa_signal_init: failed"
      sys.exit(1)

    pa_signal_new(2,  self.pa_signal_cb, None)
    pa_signal_new(15, self.pa_signal_cb, None)

    self.pa_proplist_p = pa_proplist_new()
    self.set_proplist({
        "window.name":          self.client_name,
        "media.name":           self.client_name,
        "application.name":     self.client_name
    })

    #self.pa_context_p = pa_context_new_with_proplist(
    #                            self.pa_mainloop_api_p,
    #                            self.client_name)
    self.pa_context_p = pa_context_new_with_proplist(
                                self.pa_mainloop_api_p,
                                self.client_name,
                                self.pa_proplist_p)

    if self.pa_context_p == None:
      print "pa_context_new: failed"
      sys.exit(1)

    pa_context_set_state_callback(
                self.pa_context_p,
                # This is WRONG way to do this if cb is method
                #PA_STATE_NOTIFY_CB_T(self.on_state_notify_cb),
                self.pa_state_notify_cb,
                None)

    pass

    #
    # __init__ end
    #
    
  def pulseConnect(self, server = None):
    #print "PulseGlib.pulseConnect:"

    ret = pa_context_connect(
        self.pa_context_p,
        server,
        PA_CONTEXT_NOFLAGS,
        None)

    if ret != 0:
      print "pa_context_connect: failed"
      return False

    #print "connected to", pa_context_get_server(self.pa_context_p), \
    #      "protocol", pa_context_get_protocol_version(self.pa_context_p)

    #
    # pulseConnect end
    #

  def pulseDisconnect(self):
    print "PulseGlib.pulseDisconnect:"
    if self.pa_context_p != None:
      pass

    ret = c_int(0)
    #pa_mainloop_quit(self.pa_mainloop_p, byref(ret))

    pa_glib_mainloop_free(self.pa_mainloop_p)

    #
    # pulseDisconnect end
    #


  def set_proplist(self, props_dict):
    for key in props_dict.keys():
      pa_proplist_sets(self.pa_proplist_p, key, props_dict[key])

    pass

    #
    # set_proplist end
    #

  def pa_context_get_sink_info_list(self):
    pa_operation_p = pa_context_get_sink_info_list(
        self.pa_context_p,
        self.pa_sink_info_cb,
        None
    )

    #
    # pa_context_get_sink_info_list end
    #

  def pa_context_get_sink_input_info_list(self):
    pa_operation_p = pa_context_get_sink_input_info_list(
        self.pa_context_p,
        self.pa_sink_input_info_cb,
        None
    )

    pass

    #
    # pa_context_get_sink_input_info_list end
    #

  def pa_context_set_sink_volume_by_index(self, index, pa_cvolume):
    pa_operation_p = pa_context_set_sink_volume_by_index(
        self.pa_context_p,
        index,
        byref(pa_cvolume),
        self.pa_context_success_cb,
        None
    )

    pass

    #
    # pa_context_set_sink_volume_by_index end
    #

  def pa_context_set_sink_input_volume(self, index, pa_cvolume):
    pa_operation_p = pa_context_set_sink_input_volume(
        self.pa_context_p,
        index,
        byref(pa_cvolume),
        self.pa_context_success_cb,
        None
    )

    pass

    #
    # pa_context_set_sink_input_volume end
    #

  def pa_context_set_sink_mute_by_index(self, index, mute):
    pa_operation_p = pa_context_set_sink_mute_by_index(
        self.pa_context_p,
        index,
        int(mute),
        self.pa_context_success_cb,
        None
    )

    pass

    #
    # pa_context_set_sink_mute_by_index end
    #

  def pa_context_set_sink_input_mute(self, index, mute):
    pa_operation_p = pa_context_set_sink_input_mute(
        self.pa_context_p,
        index,
        int(mute),
        self.pa_context_success_cb,
        None
    )

    pass

    #
    # pa_context_set_sink_input_mute end
    #

  def pa_cvolume_max(self, pa_cvolume):
    return pa_cvolume_max(byref(pa_cvolume))
    pass

    #
    # pa_cvolume_max end
    #

  def pa_sw_volume_to_linear(self, pa_volume_t):
    return pa_sw_volume_to_linear(pa_volume_t)
    pass

    #
    # pa_sw_volume_to_linear end
    #

  def pa_sw_volume_from_linear(self, d):
    return pa_sw_volume_from_linear(d)
    pass

    #
    # pa_sw_volume_from_linear end
    #

  def pa_cvolume_scale(self, pa_cvolume, pa_volume_t):
    return pa_cvolume_scale(byref(pa_cvolume), pa_volume_t)

    #
    # pa_cvolume_scale end
    #

  def pa_context_ready(self):

    if not self.subscribe_done:
      pa_operation_p = pa_context_subscribe(
          self.pa_context_p,
          PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_SINK,
          self.pa_context_success_cb,
          None)

      pa_context_set_subscribe_callback(
              self.pa_context_p,
              self.pa_context_subscribe_cb,
              None
      )
      self.subscribe_done = True

    pass

    #
    # pa_context_ready end
    #

  # Callback
  def on_state_notify_cb(self, pa_context_p, userdata_p):
    state = pa_context_get_state(pa_context_p)

    if state == PA_CONTEXT_UNCONNECTED:
      print "PA_CONTEXT_UNCONNECTED"

    elif state == PA_CONTEXT_CONNECTING:
      self.emit("context-connecting")

    elif state == PA_CONTEXT_AUTHORIZING:
      self.emit("context-authorizing")

    elif state == PA_CONTEXT_SETTING_NAME:
      self.emit("context-setting-name")

    elif state == PA_CONTEXT_READY:
      self.pa_context_ready()
      self.emit("context-ready")

    elif state == PA_CONTEXT_FAILED:
      self.emit("context-failed")

    elif state == PA_CONTEXT_TERMINATED:
      self.emit("context-terminated")

    pass

    #
    # on_state_notify_cb end
    #

  def on_context_success_cb(self, pa_context_p, success, userdata_p):
    self.emit("context-success", success)

    pass

    #
    # on_context_success_cb end
    #

  def on_context_subscribe_cb(self, pa_context_p,
                                    event_type, index, userdata_p):
    if event_type == (PA_SUBSCRIPTION_EVENT_SINK |
                      PA_SUBSCRIPTION_EVENT_NEW):
      self.emit("sink-new", index)
    elif event_type == (PA_SUBSCRIPTION_EVENT_SINK_INPUT |
                        PA_SUBSCRIPTION_EVENT_NEW):
      self.emit("sink-input-new", index)
    elif event_type == (PA_SUBSCRIPTION_EVENT_SINK |
                        PA_SUBSCRIPTION_EVENT_CHANGE):
      self.emit("sink-change", index)
    elif event_type == (PA_SUBSCRIPTION_EVENT_SINK_INPUT |
                        PA_SUBSCRIPTION_EVENT_CHANGE):
      self.emit("sink-input-change", index)
    elif event_type == (PA_SUBSCRIPTION_EVENT_SINK |
                        PA_SUBSCRIPTION_EVENT_REMOVE):
      self.emit("sink-remove", index)
    elif event_type == (PA_SUBSCRIPTION_EVENT_SINK_INPUT |
                        PA_SUBSCRIPTION_EVENT_REMOVE):
      self.emit("sink-input-remove", index)

    pass

    #
    # on_context_subscribe_cb end
    #

  def on_sink_info_cb(self, pa_context_p, pa_sink_info_p, eol, userdata_p):
    self.emit("sink-info", pa_sink_info_p, eol)
    pass

    #
    # on_sink_info_cb end
    #

  def on_sink_input_info_cb(self, pa_context_p, pa_sink_input_info_p, eol,
                            userdata_p):
    self.emit("sink-input-info", pa_sink_input_info_p, eol)
    pass

    #
    # on_sink_input_info_cb end
    #
  def on_signal_cb(self, pa_mainloop_api_p, pa_signal_event_p, sig, userdata_p):
    if sig == 2 or sig == 15:
      self.pulseDisconnect()
      gtk.main_quit()

    pass

    #
    # on_signal_cb end
    #

  #
  # class PulseGlib end
  #

gobject.type_register(PulseGlib)

if __name__ == "__main__":

  def sink(sender, sink_p, last):
    if last != 1:
      sink = sink_p[0]
      print last, sink

  def sink_input(sender, sink_p, last):
    if last != 1:
      sink = sink_p[0]
      print last, sink
      #pulse.pa_cvolume_scale(sink.volume, 0)
      #pulse.pa_context_set_sink_input_volume(sink.index, sink.volume)

  def ok(sender):
    print "connection ok"
    pulse.pa_context_get_sink_info_list()
    pulse.pa_context_get_sink_input_info_list()

  pulse = PulseGlib()
  pulse.connect("context-ready", ok)
  pulse.connect("sink-info", sink)
  pulse.connect("sink-input-info", sink_input)

  pulse.pulseConnect()
  gtk.main()

